/*********************************************************************
This is a library for our Monochrome OLEDs based on SSD1305 drivers

  Pick one up today in the adafruit shop!
  ------> https://www.adafruit.com/products/2675

These displays use I2C or SPI to communicate

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

Written by Limor Fried/Ladyada  for Adafruit Industries.  
BSD license, check license.txt for more information
All text above, and the splash screen below must be included in any redistribution
*********************************************************************/



#define pgm_read_byte(addr) (*(const unsigned char *)(addr))

#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <stdlib.h>
#include <string.h>

#include "Adafruit_GFX.h"
#include "../Adafruit_SSD1305-Library/Adafruit_SSD1305.h"
#include "glcdfont.c"
#include "sdkconfig.h"

static char tag[] = "Adafruit_SSD1305";

#ifdef SPI_HAS_TRANSACTION
SPISettings oledspi = SPISettings(4000000, MSBFIRST, SPI_MODE0);
#else
#define ADAFRUIT_SSD1305_SPI SPI_CLOCK_DIV2
#endif

#if !defined(_BV)
#define _BV(x) (1 << (x))
#endif // _BV

// a 5x7 font table
extern const uint8_t font[];

// the memory buffer for the LCD

static uint8_t buffer[SSD1305_LCDHEIGHT * SSD1305_LCDWIDTH / 8] = { 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x3E, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0xF0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xE0, 0xE0, 0xE0,
0xE0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x1F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFD,
0xFB, 0x7B, 0xBF, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xF7, 0xF7, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xE0, 0xE0, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
#if (SSD1305_LCDHEIGHT == 64)
0xFE, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF1,
0xC1, 0x01, 0x00, 0x00, 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8,
0xF0, 0x00, 0x00, 0xF0, 0xF8, 0xFC, 0xFC, 0x3C, 0x3C, 0x3C, 0x3C, 0x78, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x70, 0x78, 0x7C, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFC, 0xFC, 0xF8, 0xF0, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3D, 0x3D, 0x3D, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x70, 0x38,
0x3C, 0x3C, 0x3C, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC,
0xFC, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x03,
0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0xFC, 0xFE, 0xFF, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xE0, 0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9,
0xF9, 0xF8, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9,
0xF8, 0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8,
0xF8, 0xF9, 0x09, 0xF9, 0x09, 0xD8, 0xB8, 0x78, 0x08, 0xF8, 0x09, 0xE9, 0xE9, 0x19, 0xF8, 0x08,
0xF8, 0xF8, 0x08, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, 0xF9, 0xE9, 0x09, 0xE9, 0xF8, 0x09, 0xA9, 0x29,
0xD9, 0xF8, 0x08, 0xF9, 0x09, 0xA9, 0xA9, 0xF8, 0xD8, 0xA8, 0xA9, 0x69, 0xF9, 0xF9, 0xF9, 0xF9,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01
#endif
};

Adafruit_SSD1305::Adafruit_SSD1305(
	int8_t MOSI,
	int8_t SCLK,
	int8_t DC,
	int8_t RST,
	int8_t CS) : Adafruit_GFX(SSD1305_LCDWIDTH, SSD1305_LCDHEIGHT) {
	mosi = MOSI;
  sclk = SCLK;
  dc   = DC;
  rst  = RST;
  cs   = CS;
}


// the most basic function, set a single pixel
void Adafruit_SSD1305::drawPixel(int16_t x, int16_t y, uint16_t color) {
  if ((x >= width()) || (y >= height()) || (x < 0) || (y < 0)) {
    return;
  }

  // check rotation, move pixel around if necessary
  switch (getRotation()) {
  case 1:
    adagfx_swap(x, y);
    x = WIDTH - x - 1;
    break;
  case 2:
    x = WIDTH - x - 1;
    y = HEIGHT - y - 1;
    break;
  case 3:
    adagfx_swap(x, y);
    y = HEIGHT - y - 1;
    break;
  }  

  // x is which column
  if (color == WHITE) {
    buffer[x+ (y/8)*SSD1305_LCDWIDTH] |= _BV((y%8));
  }
  else {
    buffer[x+ (y/8)*SSD1305_LCDWIDTH] &= ~_BV((y%8));
  }
}

void Adafruit_SSD1305::begin() {

	ESP_LOGI(tag, "test abcd: sclk: %d, din: %d", sclk, mosi);
	spi_bus_config_t bus_config;
	bus_config.sclk_io_num   = sclk; // CLK
	bus_config.mosi_io_num   = mosi; // MOSI
	bus_config.miso_io_num   = -1; // MISO
	bus_config.quadwp_io_num = -1; // Not used
	bus_config.quadhd_io_num = -1; // Not used
	ESP_LOGI(tag, "... Initializing bus.");
	ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &bus_config, 1));

	spi_device_interface_config_t dev_config;
	dev_config.address_bits     = 0;
	dev_config.command_bits     = 0;
	dev_config.dummy_bits       = 0;
	dev_config.mode             = 0;
	dev_config.duty_cycle_pos   = 0;
	dev_config.cs_ena_posttrans = 0;
	dev_config.cs_ena_pretrans  = 0;
	dev_config.clock_speed_hz   = 100000; // 100KHz
	dev_config.spics_io_num     = cs;
	dev_config.flags            = 0;
	dev_config.queue_size       = 1;
	dev_config.pre_cb           = NULL;
	dev_config.post_cb          = NULL;
	ESP_LOGI(tag, "... Adding device bus.");
	ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &spi_handle));

  
  gpio_set_direction((gpio_num_t)dc, GPIO_MODE_OUTPUT);
  gpio_set_direction((gpio_num_t)rst, GPIO_MODE_OUTPUT);
    
  gpio_set_level((gpio_num_t)rst, 1);
  // VDD (3.3V) goes high at start, lets just chill for a ms
  vTaskDelay(1/portTICK_PERIOD_MS);
  // bring reset low
  gpio_set_level((gpio_num_t)rst, 0);
  // wait 10ms
  vTaskDelay(10/portTICK_PERIOD_MS);
  // bring out of reset
  gpio_set_level((gpio_num_t)rst, 1);


#if defined SSD1306_128_32
  // Init sequence for 128x32 OLED module
  command(SSD1305_DISPLAYOFF);                    // 0xAE
  command(SSD1305_SETLOWCOLUMN | 0x0);  // low col = 0
  command(SSD1305_SETHIGHCOLUMN | 0x0);  // hi col = 0
  command(SSD1305_SETSTARTLINE | 0x0); // line #0
  command(0x2E); //??
  command(SSD1305_SETCONTRAST);                   // 0x81
  command(0x32);
  command(SSD1305_SETBRIGHTNESS);                 // 0x82
  command(0x80);
  command(SSD1305_SEGREMAP | 0x01);
  command(SSD1305_NORMALDISPLAY);                 // 0xA6
  command(SSD1305_SETMULTIPLEX);                  // 0xA8
  command(0x3F); // 1/64
  command(SSD1305_MASTERCONFIG);
  command(0x8e); /* external vcc supply */
  command(SSD1305_COMSCANDEC);
  command(SSD1305_SETDISPLAYOFFSET);              // 0xD3
  command(0x40); 
  command(SSD1305_SETDISPLAYCLOCKDIV);            // 0xD5
  command(0xf0); 
  command(SSD1305_SETAREACOLOR);  
  command(0x05);
  command(SSD1305_SETPRECHARGE);                  // 0xd9
  command(0xF1);
  command(SSD1305_SETCOMPINS);                    // 0xDA
  command(0x12);

  command(SSD1305_SETLUT);
  command(0x3F);
  command(0x3F);
  command(0x3F);
  command(0x3F);

  #endif

  #if defined SSD1305_128_64
  // Init sequence for 128x64 OLED module
  command(SSD1305_DISPLAYOFF);                    // 0xAE
  command(SSD1305_SETLOWCOLUMN | 0x4);  // low col = 0
  command(SSD1305_SETHIGHCOLUMN | 0x4);  // hi col = 0
  command(SSD1305_SETSTARTLINE | 0x0); // line #0
  command(0x2E); //??
  command(SSD1305_SETCONTRAST);                   // 0x81
  command(0x32);
  command(SSD1305_SETBRIGHTNESS);                 // 0x82
  command(0x80);
  command(SSD1305_SEGREMAP | 0x01);
  command(SSD1305_NORMALDISPLAY);                 // 0xA6
  command(SSD1305_SETMULTIPLEX);                  // 0xA8
  command(0x3F); // 1/64
  command(SSD1305_MASTERCONFIG);
  command(0x8e); /* external vcc supply */
  command(SSD1305_COMSCANDEC);
  command(SSD1305_SETDISPLAYOFFSET);              // 0xD3
  command(0x40); 
  command(SSD1305_SETDISPLAYCLOCKDIV);            // 0xD5
  command(0xf0); 
  command(SSD1305_SETAREACOLOR);  
  command(0x05);
  command(SSD1305_SETPRECHARGE);                  // 0xd9
  command(0xF1);
  command(SSD1305_SETCOMPINS);                    // 0xDA
  command(0x12);

  command(SSD1305_SETLUT);
  command(0x3F);
  command(0x3F);
  command(0x3F);
  command(0x3F);
  #endif

  command(SSD1305_DISPLAYON);//--turn on oled panel
}

void Adafruit_SSD1305::invertDisplay(uint8_t i) {
  if (i) {
    command(SSD1305_INVERTDISPLAY);
  } else {
    command(SSD1305_NORMALDISPLAY);
  }
}

void Adafruit_SSD1305::command(uint8_t c) { 
	gpio_set_level((gpio_num_t)dc, 0);
  vTaskDelay(1/portTICK_PERIOD_MS);
   //delay(1);
  spixfer(c);
}

void Adafruit_SSD1305::data(uint8_t c) {
 	gpio_set_level((gpio_num_t)dc, 1);
  spixfer(c);
}

void Adafruit_SSD1305::display(void) {
  uint16_t i=0;
  uint8_t page;
  if (SSD1305_LCDHEIGHT == 64) page = 0;
  if (SSD1305_LCDHEIGHT == 32) page = 4;

  for(; page<8; page++) {
    command(SSD1305_SETPAGESTART + page);
    command(0x00);
    command(0x10);
    gpio_set_level((gpio_num_t)dc, 1);
      
    for(uint8_t x=0; x<128; x++) {
    	spixfer(buffer[i++]);
    }
  }
}

// clear everything
void Adafruit_SSD1305::clearDisplay(void) {
  memset(buffer, 0, (SSD1305_LCDWIDTH*SSD1305_LCDHEIGHT/8));
}


void Adafruit_SSD1305::spixfer(uint8_t x) {
	spi_transaction_t trans_desc;
	trans_desc.address   = 0;
	trans_desc.command   = 0;
	trans_desc.flags     = 0;
	trans_desc.length    = 8;
	trans_desc.rxlength  = 0;
	trans_desc.tx_buffer = &x;
	trans_desc.rx_buffer = NULL;

	ESP_ERROR_CHECK(spi_device_transmit(spi_handle, &trans_desc));
}
